/**************************************
 *
 * IDS estatico modificado de tarakanov
 * v0.3
 * autor: Juan Iturbe
 *
 * Notas de la version:
 * v0.1
 * Se cambia cells[].estado=0 por eliminar la celula
 *
 * v0.2
 * Se agrega finalmente la inmunizacion
 *
 * v0.3
 * Se ingresa por argumento el archivo a analizar y sus tipos
 *
 *
 *
 */





#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <map>
#include <vector>
#include <algorithm>



#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include "gsl/gsl_linalg.h"


using namespace std;


ofstream tdc("tabla_de_contingencia.dat",ios::out);

class Celula
{
public:

   int id;     //Identificador de la celula
   double x;   //Coordanada x
   double y;   //Coordenada y
   double z;   //Coordenada z
   int f;      //el tipo
   int estado; // 0: muerta ; 1: viva

   Celula(double xx, double yy, double zz, int ff,int e)
   {
     x=xx;
     y=yy;
     z=zz;
     f=ff;
     estado=e;
   }

   Celula(){;}
};
class Tabla_de_contingencia
{
public:
   int id;
   int vp;
   int vn;
   int fp;
   int fn;
   int otro;
   int total;

   Tabla_de_contingencia(int idd)
   {
     id=idd;
     vp=0;
     vn=0;
     fp=0;
     fn=0;
     otro=0;
   }

   Tabla_de_contingencia(){
   
   
   vp=0;
   vn=0;
   fp=0;
   fn=0;
   total=0;
   }
};



int split(vector<string>& v, const string& str, char c)
{
v.clear();
string::const_iterator s = str.begin();
while (true) {
  string::const_iterator begin = s;

  while (*s != c && s != str.end()) { ++s; }

    v.push_back(string(begin, s));

    if (s == str.end()) {
      break;
    }
    if (++s == str.end()) {
      v.push_back("");
      break;
    }
}

return v.size();

}
std::string convert( double x )
{
   std::ostringstream oss;
   oss << x;
   return oss.str();
}

void resume(map<int,int> verdaderos,map<int,int> obtenidos){


ifstream parametros2("parametros.dat");
ofstream log_resumen("resumen.log",ios::out);


map< int, Tabla_de_contingencia> tabla;

int vp=0;
int vn=0;
int fp=0;
int fn=0;
int otro=0;
int tam=verdaderos.size();
string linea;

tdc << "PARAMETROS" << endl;

while(!parametros2.eof()){

     getline(parametros2,linea);
     tdc << linea << endl;

}

tdc << "TABLA DE CONTINGENCIA" << endl;

for(int i=0;i<tam;i++){

 log_resumen << i << ": ";
 if( obtenidos[i] >= 1 && verdaderos[i] >= 1){
   log_resumen << "Verdadero Positivo: " <<  obtenidos[i] << " & " << verdaderos[i] << endl;
   tabla[verdaderos[i]].vp++;
   vp++;

 }else if( obtenidos[i]==0 && verdaderos[i]==0){

   log_resumen << "Verdadero Negativo: " <<  obtenidos[i] << " & " << verdaderos[i] << endl;

   tabla[verdaderos[i]].vn++;
   vn++;

 }else if( obtenidos[i]>=1 && verdaderos[i]==0){

   log_resumen << "Falso Positivo: " <<  obtenidos[i] << " & " << verdaderos[i] << endl;

   tabla[verdaderos[i]].fp++;
   fp++;

 }else if( obtenidos[i]==0 && verdaderos[i]>=1){

   log_resumen << "Falso Negativo: " <<  obtenidos[i] << " & " << verdaderos[i] << endl;

   tabla[verdaderos[i]].fn++;
   fn++;

 }else{


   log_resumen << "Otro: " <<  obtenidos[i] << " & " << verdaderos[i] << endl;

   tabla[verdaderos[i]].otro++;
   otro++;



 }

   tabla[verdaderos[i]].total++;

}

double exactitud=(double)(vp+vn)/tam;
double error = (double)(fp+fn)/tam;
double sensibilidad = (double)vp/(vp+fn);
double especificidad = (double)vn/(fp+vn);
double vpp= (double)vp/(vp+fp);
double vpn= (double)vn/(vn+fn);


cout << "RESULTADO GENERAL" << endl << endl;
cout << "De un total de " << tam << endl << endl;
cout << vp << " Verdaderos Positivos." << endl;//Dio normal y era normal 
cout << fp << " Falsos Positivos." << endl;    //Dio anormal y era normal
cout << fn << " Falsos Negativos." << endl;    //Dio normal  y era anormal
cout << vn << " Verdaderos Negativos." << endl << endl;//Dio anormal y era anormal
cout << otro << " Otros." << endl << endl;
cout << "vp+fp = " << vp + fp << endl;

cout << "fn+vn = " << fn + vn << endl;

cout << "vp+fn = " << vp + fn << endl;
cout << "fp+vn = " << fp + vn << endl;


cout << "Exactitud = " << exactitud << endl;
cout << "Error = " << error << endl;
cout << "Sensibilidad = " << sensibilidad << endl;
cout << "Especificidad = " << especificidad << endl;
cout << "Valor predictivo: Positivo = " << vpp << endl;
cout << "Valor predictivo: Negativo = " << vpn << endl;



cout << endl;
cout << endl;


tdc << "RESULTADOS" << endl << endl;
tdc << "De un total de " << tam-1 << endl << endl;
tdc << vp << " Verdaderos Positivos." << endl;//Dio normal y era normal 
tdc << fp << " Falsos Positivos." << endl;    //Dio anormal y era normal
tdc << fn << " Falsos Negativos." << endl;    //Dio normal  y era anormal
tdc << vn << " Verdaderos Negativos." << endl << endl;//Dio anormal y era anormal
tdc << otro << " Otros." << endl << endl;
tdc << "vp+fp = " << vp + fp << endl;
tdc << "vp+fp = " << vp + fp << endl;
tdc << "fn+vn = " << fn + vn << endl;
tdc << "vp+fn = " << vp + fn << endl;
tdc << "fp+vn = " << fp + vn << endl;


tdc << "Exactitud = " << exactitud << endl;
tdc << "Error = " << error << endl;
tdc << "Sensibilidad = " << sensibilidad << endl;
tdc << "Especificidad = " << especificidad << endl;
tdc << "Valor predictivo: Positivo = " << vpp << endl;
tdc << "Valor predictivo: Negativo = " << vpn << endl;



tdc << endl;
tdc << endl;

ofstream tdc_detalle("tdc_detalle.resultado",ios::out);
map < int, Tabla_de_contingencia >::iterator iter;

tdc_detalle << "id,vp,fp,fn,vn" << endl;

 for(iter=tabla.begin();iter!=tabla.end();iter++){

    tdc_detalle << iter->first << "," << iter->second.vp << "," << iter->second.fp << "," << iter->second.fn << "," << iter->second.vn << endl;


 }


}






int main(int argc, char *argv[ ]){

   int i,j, caract,lineas,f,lf,la,r,m,n,q;
   time_t comienzo, final;
   struct tm *tiempoComienzoPtr, *tiempoFinalPtr;
   string linea;
   double umbral_afinidad;



   //ifstream tipos_verd("salida_tipos.default");
   //ifstream analisis("salida.default");

   cout << "Archivo a analizar: " << argv[1] << endl;
   ifstream analisis(argv[1]);

   cout << "Tipos del archivo a analizar: " << argv[2] << endl;
   ifstream tipos_verd(argv[2]);

   
   cout << "Tipos del archivo motor: " << argv[3] << endl;
   ifstream tipos_mot(argv[3]);

 


//tipos motor
//   ifstream tipos_mot("salida_tipos.default");

   ofstream resultados("resultados.csv",ios::out);
   ofstream log("estatico.log",ios::out);
   ifstream parametros("parametros.dat");

   map < string , string > param;

     log << "PARAMETROS" <<endl;


   vector<string> vec2(2);

   while(!parametros.eof()){

     getline(parametros,linea);
     split(vec2, linea, '=');
     param[vec2[0]]=vec2[1];

      log << linea << endl;

      tdc << linea << endl;
   }


   m=atoi(param["m"].c_str());
   n=atoi(param["n"].c_str());
   q=atoi(param["q"].c_str());
   umbral_afinidad=atof(param["umbral_afinidad"].c_str());



   vector<string> vec(n);
   caract=n;
 
   lineas=0;
   r=0;




//GSL
   gsl_vector *work, *b, *x,*V_S;
   gsl_matrix *A,*Original2, *R, *S, *Original,*y,*X;


   gsl_set_error_handler(NULL);
   work= gsl_vector_alloc( n );
   V_S=gsl_vector_alloc(n);
   y=gsl_matrix_alloc(m,q);
   fprintf( stderr, "got to line %d\n", __LINE__ );
   A= gsl_matrix_alloc( m, n );
   X= gsl_matrix_alloc( n, n );
   Original= gsl_matrix_alloc( m, n );
   Original2= gsl_matrix_alloc( m, n );
   R= gsl_matrix_alloc( n, n );
   fprintf( stderr, "got to line %d\n", __LINE__ );
   
   if( !work || !A || !R || !V_S ){
      fprintf( stderr, "fail\n" );
      gsl_vector_free( work );
      gsl_vector_free( V_S );
      gsl_matrix_free( A );
      gsl_matrix_free( R );
      exit( EXIT_FAILURE );
  }

//FIN GSL




map<int, vector<string> > matriz;
map<int, vector<double> > matriz2;


/*
 if(!strcmp(argv[i],"-h")){
   cout << endl<< endl;
   cout << "---=== AYUDA ===---" << endl << endl;
   cout << "Modo de ejecucion" << endl;
   cout << "./estatico -f <nombre_del_archivo_del_motor> (-lf <num_de_lineas_de_archivo_motor>) -a <nombre_del_archivo_analizar> (-la <num_de_lineas_a_analizar>) (-r <archivo_resultados>)\n" << endl;
   cout << "Nota: Comandos entre parentesis, opcionales" << endl << endl;
   cout << "Opciones:" << endl;
   cout << "-f:  Nombre del archivo que se procesara para el motor de deteccion estatico" << endl;
   cout << "-lf: Numero de lineas que se usaran del archivo del motor de deteccion estatico, sino se utiliza todo el archivo" << endl;
   cout << "-a:  Nombre del archivo que se analizara" << endl;
   cout << "-la: Numero de lineas que se analizaran del archivo, sino se analiza todo el archivo" << endl;
   cout << "-r:  Resultados del experimento" << endl;
   cout << endl << endl;
   return -1;

 }
*/



fprintf( stderr, "got to line %d\n", __LINE__ );


float valor;
j=0;
map<int,int> tipos_verdaderos,tipos_motor,tipos_analisis;

//Los tipos del motor que vienen por entrada estandar
//
while(!tipos_mot.eof()){

  getline(tipos_mot,linea);
  tipos_motor[j]=atoi(linea.c_str());

  j++;


}

j=0;
//Se sacan los tipos verdaderos previamente parseados (por parser)

while (! tipos_verd.eof())
{
  getline (tipos_verd,linea);

  tipos_verdaderos[j]=atoi(linea.c_str());

  j++;

}

fprintf( stderr, "got to line %d\n", __LINE__ );


j=0;

//Se setea la matriz A con los datos

while ( j < m){

cin >> linea;

  split(vec,linea,',');


  for(int k=0;k<n;k++){

    valor=atof(vec[k].c_str());
//    cout << valor << endl;
    gsl_matrix_set( A, j, k, valor );
  }

j++;
}




log << "j: " << j << endl;

fprintf( stderr, "got to line %d\n", __LINE__ );

gsl_matrix_memcpy(Original, A);
gsl_matrix_memcpy(Original2, A);

fprintf( stderr, "got to line %d\n", __LINE__ );

comienzo = time( NULL );
cout << "Calculando SVD..." << endl;

//Se saca la Descomposicion de Valores Singulares de la matriz A;
//Se cambia el metodo gsl_linalg_SV_decomp por uno modificado donde funciona mas rapido con matrices donde m >> n (m mucho mayor a n)


//if(  gsl_linalg_SV_decomp( A, R, V_S, work )  ){

if(  gsl_linalg_SV_decomp_mod( A,X, R, V_S, work )  ){
    fprintf( stderr, "fail\n" );
    gsl_vector_free( work );
    gsl_vector_free(V_S);
    gsl_matrix_free( A );
    gsl_matrix_free( R );
    gsl_matrix_free( X );
    exit( EXIT_FAILURE );
}

cout << "SVD listo" << endl;
final = time( NULL );
printf( "Tiempo SVD: %f s\n", difftime(final, comienzo) );



//V:   Matriz de Vectores derecho
//V_S: Valores singulares
//y:   Matriz de m datos de q dimensiones


//Se cambia de dimension segun Tarakanov
for (int k=0;k<q;k++) {
      cout << "\ndimension q: " << k << "\n";

      for (int l=0;l<m;l++){

          //y[l][k]=0.0;
          gsl_matrix_set(y,l,k,0.0);

          for (j=0;j<n;j++){
            //y[l][k] += gsl_matrix_get(Original,l,j)*gsl_matrix_get(V,j,k)
	    gsl_matrix_set(y,l,k,gsl_matrix_get(y,l,k)+gsl_matrix_get(Original,l,j)*gsl_matrix_get(R,j,k));
	
          }
	  //y[l][k] *= (1/gsl_vector_get (V_S,k));
          gsl_matrix_set(y,l,k,gsl_matrix_get(y,l,k)*(1/gsl_vector_get (V_S,k)));

      }
}


map<int,Celula> cells;
map<int,Celula> copia_cells;
Celula cell;


int u=0;

for(j=0;j<m;j++){


  cell.id=j;
  cell.x=gsl_matrix_get(y,j,0);
  cell.y=gsl_matrix_get(y,j,1);
  cell.z=gsl_matrix_get(y,j,2);
  cell.f=tipos_motor[j];
  cell.estado=1;
  cells[j]=cell;
  u++;
}
copia_cells=cells;


double dist_aux;
Celula mas_cercana;
double distancia_cercana;
vector<int> cel_vivas;


ofstream representantes_apoptosis("representantes_apoptosis.dat",ios::out);

ofstream representantes_inmunes("representantes_inmunes.dat",ios::out);

comienzo = time( NULL );
log << "Calculando Apoptosis..." << endl;
log <<  "m: " << m << endl;
double xd,yd,zd;
cout << "Las vivas actuales son: " << cells.size() << endl;

map < int, Celula >::iterator iter;
map < int, Celula >::iterator iter2;

map < int, Celula >::iterator iter_aux;

mas_cercana.id=-1;

for(iter=cells.begin();iter!=cells.end();iter++){

  log << "iter->first " << iter->first << endl;
  log << "iter->second.id " << iter->second.id << endl;
  log << "iter->second.f " << iter->second.f << endl;


  if(iter->second.estado==1){
    distancia_cercana=1000;


    for(iter2=cells.begin();iter2!=cells.end();iter2++){

      if(iter->second.id!=iter2->second.id && iter2->second.estado==1){

        xd=iter2->second.x-iter->second.x;
        yd=iter2->second.y-iter->second.y;
        zd=iter2->second.z-iter->second.z;
        dist_aux=xd*xd+yd*yd+zd*zd;
        dist_aux=sqrt(dist_aux);

        if(dist_aux<distancia_cercana ){

	  mas_cercana=iter2->second;
	  distancia_cercana=dist_aux;

	}
      }
    }


log << "i: " << iter->first << "\n x: " << iter->second.x << " y: " << iter->second.y << " z: " << iter->second.z <<" f: " << iter->second.f << endl;

log << "mas_cercana: " << mas_cercana.id << "\n x: " << mas_cercana.x << " y: " << mas_cercana.y << " z: " << mas_cercana.z <<" f: " << mas_cercana.f << endl;
 
log << "Los mas cercano " << iter->second.id << "-" << mas_cercana.id << " | " << distancia_cercana  << endl;


    if(distancia_cercana<umbral_afinidad && iter->second.f == mas_cercana.f){
       cells.erase(iter);
    }
  }  
}

final = time( NULL );
printf( "Apoptosis: %f s\n", difftime(final, comienzo) );

log << "Vivos despues apoptosis " << cells.size() << endl;
tdc << "Vivos despues apoptosis " << cells.size() << endl;

tdc << "Vivos despues apoptosis " << cells.size() << endl;


comienzo = time( NULL );
cout << "Calculando Inmunizacion..." << endl;

//int tam=cel_vivas.size();
//for(j=0;j<m-1;j++){
int lll=0;

for(iter=cells.begin();iter!=cells.end();iter++){

log << "lll: " << lll << endl;
lll++;

  //mas_cercana=-1;         //mas cercana
  distancia_cercana=1000.0;

  log << "iter->first " << iter->first << endl;
  log << "iter->second.id " << iter->second.id << endl;
  log << "iter->second.f " << iter->second.f << endl;

  log << "iter->second.id " << ((*iter).second).id << endl;
  //  for(i=0;i<tam;i++){
  for(iter2=copia_cells.begin();iter2!=copia_cells.end();iter2++){

    if( iter->first!=iter2->first){
      xd=iter2->second.x-iter->second.x;
      yd=iter2->second.y-iter->second.y;
      zd=iter2->second.z-iter->second.z;
      dist_aux=xd*xd+yd*yd+zd*zd;
      dist_aux=sqrt(dist_aux);

      if(dist_aux<distancia_cercana){
        iter_aux=iter2;
/*	log << " adentro de dis_aux < distancia_cercana " << endl;
	log << iter2->second.id << " " << iter2->second.f << endl;
	log << iter_aux->second.id << " " << iter_aux->second.f << endl;*/
        distancia_cercana=dist_aux;
      }
    }
  }


  log << "Inmunizacion" << endl;
  log << "i: " << iter->first << "\n x: " << iter->second.x << " y: " << iter->second.y << " z: " << iter->second.z <<" f: " << iter->second.f << endl;
  log << "mas_cercana: " << iter_aux->first << "\n x: " << iter_aux->second.x << " y: " << iter_aux->second.y << " z: " << iter_aux->second.z <<" f: " << iter_aux->second.f << endl;
  log << "Los mas cercano |" << iter->first << "-" << iter_aux->first << " | :=" << distancia_cercana  << endl;


//Autoinmunizacion

//log << "?" << cells[mas_cercana.id].id << endl;

   if( iter->second.f != iter_aux->second.f ){

     cells[iter_aux->first]=iter_aux->second; // restore cells[i]

   }
}

fprintf( stderr, "got to line %d\n", __LINE__ );
final = time( NULL );

printf( "Inmunizacion: %f s\n", difftime(final, comienzo) );
cout << "Vivos despues inmunizacion " << cells.size() << endl;

tdc << "Vivos despues inmunizacion " << cells.size() << endl;

cout << "lll: " << lll << endl;



//RECONOCIMIENTO
//
//

j=0;
ofstream log_res("res.log",ios::out);


resultados << "Es,Reconoce" << endl;

int atack,reco;
gsl_matrix *y_matrix,*matrix2;
Celula test;
int l=0;
y_matrix=gsl_matrix_alloc(1,q);
matrix2=gsl_matrix_alloc(1,n);
int jj=0;

comienzo = time( NULL );
cout << "Calculando Reconocimiento..." << endl;


int k=0;
//fprintf( stderr, "got to line %d\n", __LINE__ );
//while(jj < m){
while (! analisis.eof() )
{
  getline (analisis,linea);
  split(vec, linea, ',');

//fprintf( stderr, "got to line %d\n", __LINE__ );
  for(k=0;k<n;k++){

//    cout << k << " " << valor << endl;
    valor=atof(vec[k].c_str());

//    fprintf( stderr, "got to line %d\n", __LINE__ );
    gsl_matrix_set( matrix2, 0, k, valor );

//    fprintf( stderr, "got to line %d\n", __LINE__ );

  }
//  tipos_analisis[jj]=atoi(vec[n].c_str());



//fprintf( stderr, "got to line %d\n", __LINE__ );

  //Es
  atack=tipos_verdaderos[jj];
  //Reconoce
  for (k=0;k<q;k++) {
//    cout << "\ndimension q: " << k << "Valor Singular"  << gsl_vector_get(V_S,k) << "\n";
    gsl_matrix_set(y_matrix,l,k,0.0);

    for (int pp=0;pp<n;pp++){
         //cout << pp << endl;
         gsl_matrix_set(y_matrix,l,k,gsl_matrix_get(y_matrix,l,k)+gsl_matrix_get(matrix2,0,pp)*gsl_matrix_get(R,pp,k));
    }
    //               cout << "Dentro: " << gsl_matrix_get(y,l,k) << endl;
    gsl_matrix_set(y_matrix,l,k,gsl_matrix_get(y_matrix,l,k)*(1/gsl_vector_get (V_S,k)));

  }

  test.x=gsl_matrix_get(y_matrix,l,0);
  test.y=gsl_matrix_get(y_matrix,l,1);
  test.z=gsl_matrix_get(y_matrix,l,2);

  log << test.x << "," << test.y << ","<< test.z << endl;
  
  //  mas_cercana=-1;
  distancia_cercana=1000;
  
//  for(j=0;j<u;j++){
  for(iter=cells.begin();iter!=cells.end();iter++){
    xd=iter->second.x-test.x;
    yd=iter->second.y-test.y;
    zd=iter->second.z-test.z;
    dist_aux=xd*xd+yd*yd+zd*zd;
    dist_aux=sqrt(dist_aux);

    if(dist_aux<distancia_cercana){
      mas_cercana=iter->second;
      distancia_cercana=dist_aux;
    }
  }


if(distancia_cercana<umbral_afinidad){


   tipos_analisis[jj]=mas_cercana.f;


}else{


   tipos_analisis[jj]=37;


}

//fprintf( stderr, "got to line %d\n", __LINE__ );
//cout << "aqui " << mas_cercana.f << endl;


resultados << atack << "," << mas_cercana.f << endl;
//log_res << jj <<","<< tipos[jj]<<","<< nombre_ataque[tipos[jj]] << "," <<cells[mas_cercana].f <<","<< nombre_ataque[cells[mas_cercana].f] << endl;
jj++;
}

fprintf( stderr, "got to line %d\n", __LINE__ );

final = time( NULL );
printf( "Reconoce: %f s\n", difftime(final, comienzo) );

cout << "tipos_verdaderos: " << tipos_verdaderos.size() << endl;
cout << "tipos_analisis: " << tipos_analisis.size() << endl;

resume(tipos_verdaderos,tipos_analisis);



gsl_vector_free( work );
gsl_vector_free( V_S );
gsl_matrix_free( A );
gsl_matrix_free( R );



return 1;

}

